package org.internna.expensetracker.mvc;

import java.util.Set;
import java.util.Date;
import java.util.Locale;
import java.util.TreeSet;
import java.util.Currency;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.Collection;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import org.internna.expensetracker.model.Account;
import org.internna.expensetracker.util.DateUtils;
import org.internna.expensetracker.model.Investment;
import org.internna.expensetracker.cache.CacheStore;
import org.internna.expensetracker.model.Subcategory;
import org.internna.expensetracker.model.InvestmentPrice;
import org.internna.expensetracker.model.AccountTransaction;
import org.internna.expensetracker.model.security.UserDetails;
import org.internna.expensetracker.services.InvestmentService;
import org.internna.expensetracker.model.InvestmentTransaction;
import org.springframework.ui.ModelMap;
import org.springframework.util.CollectionUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;

@Controller
@RequestMapping("/financial/investments/**")
public class InvestmentController {

	@Autowired private CacheStore cache;
	@Autowired private InvestmentService investmentService;

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
    }

    @RequestMapping("{id}")
    public String investments(@PathVariable Long id, ModelMap modelMap) {
    	modelMap.addAttribute("id", id);
    	Account account = Account.findAccount(id);
    	UserDetails user = UserDetails.findCurrentUser();
    	if (account.belongsTo(user)) {
    		modelMap.addAttribute("investments", account.getCurrentInvestments());
    	}
    	return "investment/performance";
    }

    @RequestMapping("create")
    public String create(ModelMap modelMap) {
        return "investment/create";
    }

    @RequestMapping(value = "create", method = RequestMethod.POST)
    public String create(String type, String name, String symbol, String locale, ModelMap modelMap) {
    	String[] parts = locale.split("_");
        Investment investment = new Investment();
        investment.setName(name);
        investment.setSymbol(symbol);
        investment.setProductType(type);
        investment.setOwner(UserDetails.findCurrentUser());
        investment.setLocale(new Locale(parts[0], parts[1], ""));
        investment.persist();
        return "redirect:/financial/accounts";
    }

    @RequestMapping("add")
    public String add(ModelMap modelMap) {
    	return add(null, modelMap);
    }

    @RequestMapping("add/{id}")
    public String add(@PathVariable Long id, ModelMap modelMap) {
    	UserDetails user = UserDetails.findCurrentUser();
    	Collection<Account> accounts = user.getInvestmentAccounts();
    	if (id != null) {
    		Account account = Account.findAccount(id);
    		if (account.belongsTo(user)) {
        		modelMap.addAttribute("account", account);
        		modelMap.addAttribute("currency", account.getCurrency());
        	} else {
        		modelMap.addAttribute("currency", CollectionUtils.isEmpty(accounts) ? "EUR" : accounts.iterator().next().getCurrency());
        	}
    	}
    	modelMap.addAttribute("accounts", accounts);
    	modelMap.addAttribute("investments", Investment.findInvestmentsByOwner(user));
        return "investment/add";
    }

    @RequestMapping(value = "add", method = RequestMethod.POST)
    public String add(InvestmentTransaction transaction, double commision, ModelMap modelMap) {
    	UserDetails user = UserDetails.findCurrentUser();
    	Account account = Account.findAccount(transaction.getAccountTransaction().getAccount().getId());
    	if (account.belongsTo(user)) {
    		cache.invalidate(user);
    		investmentService.addInvestment(user, account, transaction, commision);
    	}
    	return "redirect:/financial/accounts/" + transaction.getAccountTransaction().getAccount().getId();
    }

    @RequestMapping("view/{id}")
    public String view(@PathVariable Long id, ModelMap modelMap) {
    	UserDetails user = UserDetails.findCurrentUser();
    	InvestmentTransaction transaction = InvestmentTransaction.findInvestmentTransaction(id);
    	Account account = transaction.getAccountTransaction().getAccount();
    	if (account.belongsTo(user)) {
    		modelMap.addAttribute("account", account);
    		modelMap.addAttribute("transaction", transaction);
    		modelMap.addAttribute("investments", Investment.findInvestmentsByOwner(user));
    	}
    	return "investment/view";
    }

    @RequestMapping(value = "edit", method = RequestMethod.POST)
    public String edit(InvestmentTransaction transaction, ModelMap modelMap) {
    	UserDetails user = UserDetails.findCurrentUser();
    	InvestmentTransaction loaded = InvestmentTransaction.findInvestmentTransaction(transaction.getId());
    	Account account = loaded.getAccountTransaction().getAccount();
    	if (account.belongsTo(user)) {
    		cache.invalidate(user);
    		AccountTransaction accountTransaction = loaded.getAccountTransaction();
    		if (transaction.getPrice() != null) {
    			loaded.setQuantity(transaction.getQuantity());
    			loaded.getPrice().setPrice(transaction.getPrice().getPrice());
    			accountTransaction.setAmount(new BigDecimal(transaction.getQuantity() * transaction.getPrice().getPrice()));
    		} else {
    			loaded.setPrice(null);
    			accountTransaction.setAmount(transaction.getAccountTransaction().getAmount());
    		}
    		Investment investment = Investment.findInvestment(transaction.getInvestment().getId());
    		loaded.setInvestment(investment);
    		loaded.getAccountTransaction().setPayee(investment);	
    		accountTransaction.setOperationDate(transaction.getAccountTransaction().getOperationDate());
    		accountTransaction.setReferenceNumber(transaction.getAccountTransaction().getReferenceNumber());
    		accountTransaction.setSubcategory(Subcategory.findBySubcategory(transaction.getAccountTransaction().getSubcategory().getCategory(), user));
    		loaded.merge();
    	}
    	return "redirect:/financial/accounts/" + account.getId();
    }

    @RequestMapping("remove/{id}")
    public String remove(@PathVariable Long id, ModelMap modelMap) {
    	Long accountId = null;
    	UserDetails user = UserDetails.findCurrentUser();
    	InvestmentTransaction transaction  = InvestmentTransaction.findInvestmentTransaction(id);
    	if (transaction != null) {
    		AccountTransaction accountTransaction = transaction.getAccountTransaction();
    		Account account = accountTransaction.getAccount();
    		if (account.belongsTo(user)) {
    			cache.invalidate(user);
    			accountId = account.getId();
    			accountTransaction.remove();
    		}
    	}
    	return "redirect:/financial/accounts/" + (accountId != null ? accountId : "");
    }

    @RequestMapping("update-prices")
    public String investments(ModelMap modelMap) {
    	UserDetails user = UserDetails.findCurrentUser();
    	modelMap.addAttribute("investments", Investment.findInvestmentsByOwner(user));
    	return "investment/prices";
    }

    @RequestMapping("prices/{id}")
    public String getPrices(@PathVariable Long id, ModelMap modelMap) {
    	UserDetails user = UserDetails.findCurrentUser();
    	String currency = "EUR";
    	Investment investment = Investment.findInvestment(id);
    	Set<InvestmentPrice> prices = new TreeSet<InvestmentPrice>();
    	Set<InvestmentPrice> datedPrices = new TreeSet<InvestmentPrice>();
    	if (investment.belongsTo(user)) {
    		if (investment.getLocale() != null) {
    			Locale locale = investment.getLocale();
    			Currency investmentCurrency = Currency.getInstance(locale);
    			currency = investmentCurrency.getCurrencyCode();
    		}
    		modelMap.addAttribute("investment", investment.getName());
    		prices.addAll(InvestmentPrice.findInvestmentPricesByInvestment(investment));
    		datedPrices.addAll(prices);
    		fill(datedPrices);
    	}
    	modelMap.addAttribute("id", id);
    	modelMap.addAttribute("prices", prices);
    	modelMap.addAttribute("currency", currency);
    	modelMap.addAttribute("datedPrices", datedPrices);
    	return "investment/list-prices";
    }

    protected void fill(Set<InvestmentPrice> prices) {
    	if ((prices != null) && (prices.size() > 1)) {
    		Collection<InvestmentPrice> dated = new ArrayList<InvestmentPrice>();
    		Iterator<InvestmentPrice> it = prices.iterator();
    		InvestmentPrice target, origin = it.next();
    		while (it.hasNext()) {
    			target = it.next();
    			fill(dated, origin, target);
    			origin = target;
    		}
    		prices.addAll(dated);
    	}
    }

    protected void fill(Collection<InvestmentPrice> prices, InvestmentPrice origin, InvestmentPrice target) {
    	Date[] dates = DateUtils.interval(origin.getUpdateTime(), target.getUpdateTime());
    	for (Date date :  dates) {
    		InvestmentPrice price = new InvestmentPrice();
    		price.setUpdateTime(date);
    		price.setPrice(origin.getPrice());
    		prices.add(price);
    	}
    }

    @RequestMapping(value = "prices", method = RequestMethod.POST)
    public String setPrice(Long investmentId, Long priceId, Date operationDate, Double amount, ModelMap modelMap) {
    	UserDetails user = UserDetails.findCurrentUser();
    	Investment investment = Investment.findInvestment(investmentId);
    	InvestmentPrice price = InvestmentPrice.findInvestmentPrice(priceId);
    	if (price == null) {
    		price = new InvestmentPrice();
    		investment.addPrice(price);
    	}
    	if ((investment != null) && investment.belongsTo(user) && price.belongsTo(investment)) {
    		price.setPrice(amount);
    		price.setUpdateTime(operationDate);
    		if (price.getId() != null) {
    			price.merge();
    		} else {
    			price.persist();
    		}
    	}
    	return getPrices(investmentId, modelMap);
    }

}
